package com.tfswx.ruleengine.compute.gzjy;

import com.greenpineyu.fel.FelEngine;
import com.greenpineyu.fel.context.ContextChain;
import com.greenpineyu.fel.context.FelContext;
import com.greenpineyu.fel.context.MapContext;
import com.tfswx.ruleengine.compute.bean.Bds;
import com.tfswx.ruleengine.compute.bean.DataObject;
import com.tfswx.ruleengine.compute.bean.GzBdsDTO;
import com.tfswx.ruleengine.compute.bean.Gzdy;
import com.tfswx.ruleengine.compute.bean.GzjyLog;
import com.tfswx.ruleengine.compute.bean.GzjyjgItem;
import com.tfswx.ruleengine.compute.bean.HczdItem;
import com.tfswx.ruleengine.compute.bean.RuleInfo;
import com.tfswx.ruleengine.compute.enums.BdslxEnum;
import com.tfswx.ruleengine.compute.enums.YslxEnum;
import com.tfswx.ruleengine.compute.util.CommonUtil;
import com.tfswx.ruleengine.compute.util.ExceptionUtil;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author Ricky
 * @date 2021/3/25
 */
@Slf4j
public class FelService {
    private static final String EACH_ITEM_FIELD = "$eachItem";
    private final List<DataObject> dataList;
    private final FelEngine fel;
    @Getter
    private final List<GzjyjgItem> hcjgxqList;
    @Getter
    private final GzjyLog gzjyLog;
    @Getter
    private final Gzdy gzdy;
    private final RuleInfo ruleInfo;
    @Getter
    private final String hcjgbh;

    /**
     * @param ruleInfo       规则信息
     * @param dataObjectList 需要核查的案卡数据列表
     * @param hcjgbh         核查结果编号
     */
    public FelService(RuleInfo ruleInfo, List<DataObject> dataObjectList, String hcjgbh) {
        this.ruleInfo = ruleInfo;
        gzdy = ruleInfo.getGzdy();
        String gzbm = gzdy.getGzbm();
        String gzmc = gzdy.getGzmc();
        this.hcjgbh = hcjgbh;
        // 展开从表
        dataList = zkData(dataObjectList, null);
        // bdsInfoList表达式具体内容列表，初始化后上下文中总包含所有的表达式
        fel = RuleEngineUtil.getFelEngine(ruleInfo.getBdsList(), dataObjectList);

        // 预处理核查结果
        hcjgxqList = hqGzjyjgItemList();

        FelContext context = fel.getContext();
        context.set("service", this);
        context.set("dataList", dataList);
        gzjyLog = new GzjyLog();
        gzjyLog.setGzbm(gzbm);
        gzjyLog.setGzmc(gzmc);
        gzjyLog.setGzms(gzdy.getGzms());
        gzjyLog.setLogDetailList(new ArrayList<>());
        gzjyLog.setFnLogDetailList(new ArrayList<>());
        gzjyLog.setLogs(new ArrayList<>());

        getGzjyLog().getLogs().add("正在使用规则" + gzmc + "进行数据核查");
    }

    /**
     * 递归展开数据
     *
     * @param data
     * @param parent
     * @return List<AkDataDTO>
     */
    private static List<DataObject> zkData(List<DataObject> data, DataObject parent) {
        data.forEach(item -> item.setParent(parent));
        List<DataObject> list = new ArrayList<>(data);
        for (DataObject datum : data) {
            List<DataObject> cbsj = datum.getCbsj();
            if (cbsj != null) {
                list.addAll(zkData(cbsj, datum));
            }
        }
        return list;
    }

    private List<GzjyjgItem> hqGzjyjgItemList() {
        List<HczdItem> hczdList;
        Hczd hczdTemp;
        List<GzjyjgItem> gzjyjgItems = new ArrayList<>();
        List<Hczd> hczdSet = new ArrayList<>();
        for (Bds bds : ruleInfo.getBdsList()) {
            hczdList = bds.getHczdList();
            if (bds.getBdslx() != BdslxEnum.TS || hczdList == null || hczdList.isEmpty()) {
                continue;
            }
            for (HczdItem hczdItem : hczdList) {
                hczdTemp = new Hczd();
                hczdTemp.zdmc = hczdItem.getZdmc();
                hczdTemp.bdsbm = bds.getBdsbm();
                hczdTemp.hczdItem = hczdItem;
                hczdSet.add(hczdTemp);
            }
        }
        //        String zdjybsbh = CommonUtil.getUuid();
        for (Hczd hczd : hczdSet) {
            String bmzdmc = hczd.zdmc;
            String bdsbm = hczd.bdsbm;
            HczdItem hczdItem = hczd.hczdItem;
            String[] split = bmzdmc.split("\\.");
            String stbm = split[0];
            String zdmc = split[1];
            this.dataList.forEach(item -> {
                if (!item.getStbm().equals(stbm)) {
                    return;
                }
                List<DataObject.ZdItemDTO> zdsj = item.getZdsj();
                zdsj.forEach(zd -> {
                    if (!zd.getZdmc().equals(zdmc)) {
                        return;
                    }
                    GzjyjgItem gzjyjgItem = new GzjyjgItem();
                    gzjyjgItem.setJyjgbh(CommonUtil.getUuid());
                    gzjyjgItem.setGzbm(gzdy.getGzbm());
                    gzjyjgItem.setHczdbh(zd.getHczdbh());
                    gzjyjgItem.setHcjgbh(getHcjgbh());
                    //                    gzjyjgItem.setZdjybsbh(zdjybsbh);
                    gzjyjgItem.setStbsjbs(zd.getStbsjbs());
                    gzjyjgItem.setZdbs(zd.getZdbs());
                    gzjyjgItem.setGzmc(gzdy.getGzmc());
                    gzjyjgItem.setGzms(gzdy.getGzms());
                    gzjyjgItem.setGzpz(gzdy.getGzpz());
                    gzjyjgItem.setHczdItem(hczdItem);
                    gzjyjgItem.setSffx("N");
                    gzjyjgItem.setStbm(zd.getStbm());
                    gzjyjgItem.setZdmc(zd.getZdmc());
                    gzjyjgItem.setZdzwmc(zd.getZdzwmc());
                    gzjyjgItem.setBdsbm(bdsbm);

                    gzjyjgItems.add(gzjyjgItem);
                });
            });
        }
        return gzjyjgItems;
    }

    public List<GzjyjgItem> exec() {
        String gzbm = gzdy.getGzbm();
        String gzmc = gzdy.getGzmc();
        try {
            log.info("正在使用规则{}({})进行数据核查", gzmc, gzbm);
            List<String> logs = getGzjyLog().getLogs();
            logs.add("开始核查");
            FelContext context = fel.getContext();
            exec(gzdy.getGzbds(), context);
            logs.add("核查结束");
            gzjyLog.setSfcg(true);
        } catch (Exception e) {
            // throw new AppException("规则名称：" + gzdy.getGzmc(), e);
            gzjyLog.setCwxx(ExceptionUtil.getMessage(e));
            gzjyLog.setSfcg(false);
            log.error(ExceptionUtil.getMessage(e), e);
        } finally {
            log.info("使用规则{}({})核查完成", gzmc, gzbm);
        }
        return hcjgxqList;
    }

    private void exec(GzBdsDTO gzObj, FelContext context) {
        if (gzObj == null) {
            return;
        }
        FelContext ctx = new ContextChain(context, new MapContext());
        // 设置当前规则体到上下文中，会覆盖之前上下文中的规则
        ctx.set("currGz", gzObj);
        DataObject $parent = (DataObject) ctx.get("$curr");

        List<String> logs = getGzjyLog().getLogs();

        String bds = gzObj.getBds();

        YslxEnum yslx = gzObj.getYslx();
        Object eval;
        if (yslx == YslxEnum.BLYS) {
            List<DataObject> tempList = dataList;
            if ($parent != null) {
                tempList = $parent.getCbsj();
            }
            List<DataObject> collect = tempList.stream().filter(item -> item.getStbm().equalsIgnoreCase(gzObj.getBmc())).collect(Collectors.toList());
            logs.add("进行遍历运算，找到表对象(" + gzObj.getBmc() + ")：" + collect.size() + "个");
            for (DataObject dataEntityDTO : collect) {

                ctx.set("$curr", dataEntityDTO);
                ctx.set("$data", dataEntityDTO);
                ctx.set(gzObj.getBmc(), dataEntityDTO);
                ctx.set(EACH_ITEM_FIELD, dataEntityDTO);
                // 此时表达式形如：calc("bdsx")
                logs.add("执行：" + bds);
                eval = fel.eval(bds, ctx);
                execResult(eval, gzObj, ctx);
                logs.add("执行完成：" + bds);
                ctx.set(EACH_ITEM_FIELD, null);
            }
        } else {
            DataObject d = (DataObject) ctx.get(EACH_ITEM_FIELD);
            DataObject b = null;
            Optional<DataObject> first = dataList.stream().filter(item -> item.getStbm().equals(gzObj.getBmc())).findFirst();
            if (first.isPresent()) {
                b = first.get();
            }
            if (d == null) {
                d = b;
            }
            logs.add("进行普通运算，是否找到表对象(" + gzObj.getBmc() + ")：" + (d != null && d.getStbm().equals(gzObj.getBmc())));
            // $data内为案卡，该案卡满足：案卡.实体表名=规则.表名称
            ctx.set("$data", d);
            ctx.set(gzObj.getBmc(), b);
            logs.add("执行：" + bds);
            eval = fel.eval(bds, ctx);
            execResult(eval, gzObj, ctx);
            logs.add("执行完成：" + bds);
        }
        String bdsStr = bds.replace("calc('", "").replace("')", "");
        logs.add(bdsStr + "===>验证结束(包括子级)");
    }

    /**
     * 前置规则校验后，检验后续规则表达式
     *
     * @param eval
     * @param gzObj
     * @param context
     */
    public void execResult(Object eval, GzBdsDTO gzObj, FelContext context) {
        List<String> logs = getGzjyLog().getLogs();
        String bds = gzObj.getBds().replace("calc('", "").replace("')", "");
        if (eval == null) {
            logs.add(bds + "  -->  无");
            return;
        }
        String simpleName = eval.getClass().getSimpleName();
        if (simpleName.equals(Boolean.class.getSimpleName())) {
            boolean b = (Boolean) eval;
            logs.add(bds + "  -->  " + b);
            if (b) {
                // 如果上一步表达式校验结果为真，则继续规则表达式中yes的规则表达式
                List<GzBdsDTO> yes = gzObj.getYes();
                for (GzBdsDTO obj : yes) {
                    exec(obj, context);
                }
            } else {
                List<GzBdsDTO> no = gzObj.getNo();
                for (GzBdsDTO obj : no) {
                    exec(obj, context);
                }
            }
        }
    }


    private static class Hczd {
        private String zdmc;
        private String bdsbm;
        private HczdItem hczdItem;
    }

}
